#include "MaterialEditorWidget.h"
#include <SEUi/SEUi.h>
#include <SEAssets/AssetsElem.h>
#include <SECore/Manager.h>
#include <fstream>
#include <sstream>
#include <SERender/Resourses/Loader/ShaderLoader.h>
#include <SEEdit/Widget/MaterialEditorWidget.inl>
#include <SERender/Resourses/UniformBuffer.h>
#include <SEEdit/Widget/DragDataLoader.h>
#include <SEngine/Render/RenderFeature/UBORenderFeature.h>
#include <SESerializeYAML/MaterialSerializer.h>

#include <SEDebug/Log/Log.h>

namespace SEngine
{
    MaterialEditorWidget::MaterialEditorWidget(EditLayer & layer)
        : m_layer(layer)
    {
    }
    
    void MaterialEditorWidget::UpdateImpl(int delta)
    {
        ImGui::PushID("Material Editor");
        // ImGui::Columns(2, 0, false);
        if (ImGui::Button("Save to file")) {
            SaveMaterial(m_material.Path());
        }
        ImGui::SameLine();
        if (ImGui::Button("Reload from file")) {
            ReloadMaterial();
        }
        // ImGui::Columns(1);

        ImGui::Separator();
        
        ImGui::BeginGroup();
        ImGui::Columns(2, 0, false);
        ImGui::SetColumnWidth(0, 120);
        if (ImGui::Button("Clear##Material")) {
            m_materialPathBuffer[0] = '\0';
            m_material.Reset();
            SignalLoadMaterial.Invoke(Manager::GetInstance().Get<Material>("Standard").value());
        }
        ImGui::NextColumn();
        ImGui::InputText("Material##InputMaterial", m_materialPathBuffer, sizeof(m_materialPathBuffer));
        if (ImGui::BeginDragDropTarget()) {
            if (auto assetsPayload = ImGui::AcceptDragDropPayload("MATERIAL"); assetsPayload && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) {
                auto assetsElem = (AssetsElem*)assetsPayload->Data;
                LoadMaterial(Manager::GetInstance().Get<Material>(assetsElem->rPath).value());
            }
            ImGui::EndDragDropTarget();
        }
        ImGui::Columns(1);
        ImGui::EndGroup();

        if (m_material) {
            ImGui::BeginGroup();
            ImGui::Columns(3, 0, false);
            ImGui::SetColumnWidth(0, 60);
            ImGui::SetColumnWidth(1, 60);
            if (ImGui::Button("Clear##Shader")) {
                LoadShader(Manager::GetInstance().Get<Shader>("PBR").value());
            }
            ImGui::NextColumn();
            if (ImGui::Button("Reload##Shader")) {
                ReloadShader();
            }
            ImGui::NextColumn();
            ImGui::InputText("Shader##InputShader", m_shaderPathBuffer, sizeof(m_shaderPathBuffer));
            if (ImGui::BeginDragDropTarget()) {
                if (auto assetsPayload = ImGui::AcceptDragDropPayload("SHADER"); assetsPayload && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) {
                    auto assetsElem = (AssetsElem*)assetsPayload->Data;
                    LoadShader(Manager::GetInstance().Get<Shader>(assetsElem->rPath).value());
                }
                ImGui::EndDragDropTarget();
            }
            ImGui::Columns(1);
            ImGui::EndGroup();
        }

        ImGui::Separator();

        MaterialAttribute();
        ShaderAttribute();

        ImGui::PopID();
    }
    
    void MaterialEditorWidget::MaterialAttribute()
    {
        if (!m_material) {
            return;
        }
        if (ImGui::CollapsingHeader("Material")) {
            auto & pso = m_material.Value().GetPSO();

            ImGui::Columns(2, 0, false);
            ImGui::SetColumnWidth(0, 120);
            ImGui::Text("Blend");
            ImGui::NextColumn();
            ImGui::Checkbox("##Blend_", &pso.blend);
            ImGui::Columns(1);

            ImGui::Columns(2, 0, false);
            ImGui::SetColumnWidth(0, 120);
            ImGui::Text("CullFace");
            ImGui::NextColumn();
            ImGui::Checkbox("##CullFace_", &pso.cullFace);
            ImGui::Columns(1);
            
            ImGui::Columns(2, 0, false);
            ImGui::SetColumnWidth(0, 120);
            ImGui::Text("CullFront");
            ImGui::NextColumn();
            ImGui::Checkbox("##CullFront_", &pso.cullFront);
            ImGui::Columns(1);
            
            ImGui::Columns(2, 0, false);
            ImGui::SetColumnWidth(0, 120);
            ImGui::Text("Depth");
            ImGui::NextColumn();
            ImGui::Checkbox("##Depth_", &pso.depth);
            ImGui::Columns(1);
        }
    }

    void MaterialEditorWidget::LoadMaterial(ResRef<Material> material)
    {
        m_material = material;
        strcpy_s<sizeof(m_materialPathBuffer)>(m_materialPathBuffer, m_material.Name().data());
        strcpy_s<sizeof(m_shaderPathBuffer)>(m_shaderPathBuffer, m_material.Value().GetShader().Name().data());
        SignalLoadMaterial.Invoke(m_material);
    }

    void MaterialEditorWidget::ReloadMaterial()
    {
        if (m_material) {
            m_material.Reload([](auto & path, auto & name) {
                auto mtl = std::make_unique<Material>();
                std::ifstream file(path);
                if (file.is_open()) {
                    std::stringstream ss;
                    ss << file.rdbuf();
                    mtl->Deserialize(ss.str());
                    file.close();
                }
                return std::move(mtl);
            });
        }
    }

    void MaterialEditorWidget::SaveMaterial(const std::string &path)
    {
        if (!m_material) {
            return;
        }
        std::ofstream file(path);
        if (file.is_open()) {
            file << m_material.Value().Serialize();
            file.close();
        }
    }

    void MaterialEditorWidget::LoadShader(ResRef<Shader> shader)
    {
        strcpy_s<sizeof(m_shaderPathBuffer)>(m_shaderPathBuffer, shader.Name().data());
        m_material.Value().SetShader(shader);
    }

    void MaterialEditorWidget::ReloadShader()
    {
        auto curShader = m_material.Value().GetShader();
        if (curShader) {
            curShader.Reload([](auto & path, auto & name) {
                ShaderLoader loader;
                return loader.Load(path).CreateShader(EngineUniformBlockBinder());
            });
        }
    }

    void MaterialEditorWidget::ShaderAttribute()
    {
        if (!m_material || !m_material.Value().GetShader()) {
            return;
        }
        auto shader = m_material.Value().GetShader();
        if (ImGui::CollapsingHeader("Shader")) {
            shader.Value().ForeachUniforms([this](const std::string & name, auto & type) {
                if (name.substr(0, 2) != "u_") // 剔除内置贴图
                    ShaderUniformAttribute<std::decay_t<decltype(type)>>(name, this->m_material.Value(), type);
            });
        }
    }
}